/* 现代 MMU 概念
 * 面向：
 * - 管理超过 1TB 物理内存
 * - 最多约 2G 个显式对象
 * - 更浅的页表层次
 * - 内存的访问以对象句柄为媒介
 * 
 * 显式对象：
 * - 等效的指针大小为 4Byte
 * - 由系统直接管理的对象
 * 
 * 隐式对象：
 * - 等效的指针大小为 8Byte
 * - 以显式对象为基址 4Byte + 4Byte 偏移组成的对象
 * - 不能单独释放该对象指向的内存
 * - 伴随指向的显式对象的释放而无效
 * 
 * 分区：
 * - 大小 16MB
 * - 别名为大页
 * 
 * 混合区：
 * - 内存低 8GB 地址空间
 * - 包含对象分区和数据分区
 * - 通过位指示器(位图)区分空闲/非空闲、数据/对象分区
 * - 主要存放对象分区，当对象分区耗尽时则会将其中的数据分区整理、迁移以便腾出内存用于存放对象句柄
 * - 当混合区存满了对象分区时将产生溢出中断，我们把处理该事务的职责交给系统开发人员
 * 
 * 纯数据区：
 * - 高于 8GB 的地址空间
 * - 只包含数据分区
 * - 优先从该分区分配内存填充对象句柄
 * 
 * 对象分区：
 * - 大小 16MB
 * - 不可移动
 * - 位于低 8GB 地址空间
 * - 最多容纳约 2G 个对象
 * - 包含 16K 个 1KB 的对象组
 * 
 * 数据分区：
 * - 大小 16MB
 * - 可移动、整理
 * - 可拆成 2 的次幂大小的小页
 * - 拆分的小页在分配时需要在首部记录 4Byte 的所属对象，16MB 的大页则不需要加
 * 
 * 对象组：
 * - 大小 1KB
 * - 包含 16Byte 对象组首部
 *     - 通过所属进程 id + 线程 id 保护/共享内存
 *     - 包含小页(对象组专用)的高 24 位作为对象组内成员的公共段地址
 * - 包含 252 * 4Byte 对象
 * - 当与之绑定的小页内存分配完毕时，该对象组将不能再分配对象
 * - 只有当小页不能分配所需大小的内存时，才能为该对象组所属的线程分配新的对象组
 * 
 * 对象组专用小页：
 * - 大小 64KB
 * - 满足 数据分区 中的小页说明，但该小页的所属对象指向的时对象组的首部(可以用于与其他通用小页区分)
 * - 可以被多个对象组绑定，但这些对象组必须属于同一个线程
 * - 包含对象组的对象分配与回收功能
 * 
 * 对象：
 * - 只读/读写 + 可执行类型设置
 * - 固定大小/动态增长
 * - 层次结构
 *     - 0. 分配小于 4Byte 时：直接访问该对象包含的 3 个可用的字节
 *     - 1. 分配小于   1KB 时：通过结合该对象所在对象组中的段地址 + 该对象指向偏移构成的物理地址访问内存
 *     - 2. 分配不超过 4GB 时：通过 1. 得到该对象指向的页表，再经过一级地址变换后得到的物理地址访问内存
 *     - 3. 不超过物理大小 时：联合多个 4GB 对象组合形成超大内存的对象序列，访问方式和 2. 一致
 * 
 */ 

#ifndef xpack_algo_addressing
#define xpack_algo_addressing
#pragma push_macro("xuser")
#undef  xuser
#define xuser mixc::algo_addressing::inc
#include"instruction/bit_test_and_reset.hpp"
#include"instruction/index_of_last_set.hpp"
#include"macro/xexport.hpp"
#pragma pop_macro("xuser")

namespace mixc::algo_addressing::origin {
    struct pagoff{ uxx page, offset; };

    /**
     * @brief 
     * 寻址器
     * @note 让多级页表在逻辑上连续
     */
    struct addressing{
        /**
         * @brief 
         * 可变大小的内存寻址
         * @note 适合构建动态增长型的数组
         * @param present_indicator 页存在标志位组
         * 最低置位位 n 表示该动态数组最小的块为 2^n 
         * 最高置位位 m 表示该动态数组总容量为 2^m
         * 第 n + 1 个页的容量是前 n 个页的容量之和
         * 从第一个置位位到最后一个置位位之间必须是连续的 1，也就是中间不能有空缺的页
         * @param address 逻辑地址
         * @return pagoff 页号 + 偏移的组合地址
         * @note 实际访问形如 table[r.page][r.offset]
         * @note present_indicator 不能为 0
         */
        static pagoff var(uxx present_indicator, uxx address){
            auto r          = pagoff{};

            // 约定：
            // present_indicator 形如 0x0...fff 或者 0x0...ff...0
            // - 只存在一组连续的 1
            // - low_bits 实际上就是获取低位的 0 位组，然后把它们反转成 1 位组
            //   比如 present_indicator = 0xff0 那么 low_bits 就等于 0xf（把低 4bit 由 0 变成 1）
            auto low_bits   = ~present_indicator & (present_indicator - 1);
            auto mask       = low_bits | (present_indicator & address);
            r.page          = inc::index_of_last_set(mask) + 1;
            r.offset        = address;

            if (address > low_bits){
                inc::bit_test_and_reset(& r.offset, r.page - 1);
            }
            return r;
        }

        /**
         * @brief 
         * 固定大小的内存寻址
         * @param present_indicator 为页存在标志
         * 当 2^n 大小的页存在时，第 n 位为置位态
         * 所有存在的页的加起来表示一块固定大小的内存，此后存在位不能再修改
         * @param address 逻辑地址
         * @return pagoff 页号 + 偏移的组合地址
         * @note 实际访问形如 table[r.page][r.offset]
         * @note present_indicator 不能为 0
         */
        static pagoff fixed(uxx present_indicator, uxx address){
            auto r          = pagoff{};
            auto mask       = present_indicator ^ address;
            r.page          = inc::index_of_last_set(mask);
            r.offset        = address & ((uxx(1) << r.page) - 1);
            return r;
        }
    };
}

#endif

xexport_space(mixc::algo_addressing::origin)
